package redisUtils

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
	"utilsx/constant"
)

// https://www.lsdcloud.com/go/middleware/go-viper.html
// https://www.cnblogs.com/itbsl/p/14198111.html

func GetAllKeys(rdb *redis.Client) {
	ctx := context.Background()
	//*表示获取所有的key
	keys, err := rdb.Keys(ctx, "*").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(keys)

}
func GetAllKeys2(rdb *redis.Client) {
	ctx := context.Background()
	iter := rdb.Scan(ctx, 0, "*", 0).Iterator()
	for iter.Next(ctx) {
		fmt.Println("keys", iter.Val())
	}
	if err := iter.Err(); err != nil {
		panic(err)
	}
}
func GetKeyType(rdb *redis.Client, key string) {
	ctx := context.Background()
	vType, err := rdb.Type(ctx, key).Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(vType) //string
}
func DelItem(rdb *redis.Client, key ...string) {
	ctx := context.Background()
	_, err := rdb.Del(ctx, key...).Result()
	if err != nil {
		panic(err)
	}
}

func ExistsItem(rdb *redis.Client, key ...string) (int64, bool) {
	ctx := context.Background()
	result, err := rdb.Exists(ctx, key...).Result()
	if err != nil {
		panic(err)
		return 0, false
	}
	if result <= 0 {
		return 0, false
	}
	return result, true
}
func SetExpiration(rdb *redis.Client, key string, period time.Duration) {
	ctx := context.Background()
	// 时间段
	res, err := rdb.Expire(ctx, key, period).Result()
	if err != nil {
		panic(err)
	}
	if res {
		fmt.Println("设置成功")
	} else {
		fmt.Println("设置失败")
	}

}

// GetExpiration 获取有效期
func GetExpiration(rdb *redis.Client, key string, period time.Duration) {
	ctx := context.Background()
	//获取剩余有效期,单位:秒(s)
	ttl, err := rdb.TTL(ctx, key).Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(ttl)

	//获取剩余有效期,单位:毫秒(ms)
	pttl, err := rdb.PTTL(ctx, "key").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(pttl)
}

func FlushDB(rdb *redis.Client, isAll bool) {

	ctx := context.Background()
	var res string
	var err error
	if isAll {
		res, err = rdb.FlushAll(ctx).Result()
		if err != nil {
			panic(err)
		}
	} else {
		//清空当前数据库，因为连接的是索引为0的数据库，所以清空的就是0号数据库
		res, err = rdb.FlushDB(ctx).Result()
		if err != nil {
			panic(err)
		}
	}
	if false {
		fmt.Println(res) //OK

	}
}

// 仅仅支持字符串(包含数字)操作，不支持内置数据编码功能。
// 如果需要存储Go的非字符串类型，需要提前手动序列化，获取时再反序列化。 //不存在时才会设置

func Set[T any](rdb *redis.Client, key string, value T, expiration time.Duration, mustNot bool) {
	ctx := context.Background()
	marshal, err := constant.Json.Marshal(&value)
	if err != nil {
		panic(err)
	}
	if !mustNot {
		//Set方法的最后一个参数表示过期时间，0表示永不过期
		info, err := rdb.Set(ctx, key, marshal, expiration).Result()
		if err != nil {
			panic(err)
		}
		fmt.Println(info)
	} else {
		//不存在时才会设置
		info, err := rdb.SetNX(ctx, key, marshal, expiration).Result()
		if err != nil {
			panic(err)
		}
		fmt.Println(info)

	}

}

func Get[T any](rdb *redis.Client, key string) (*T, bool) {
	ctx := context.Background()

	if _, b := ExistsItem(rdb, key); !b {
		return nil, false
	}
	val, err := rdb.Get(ctx, key).Result()
	if err != nil {
		panic(err)
		return nil, false
	}

	var result T
	err = constant.Json.Unmarshal([]byte(val), &result)
	if err != nil {
		panic(err)
		return nil, false
	}
	return &result, true

}

/*
Incr():增加+1
IncrBy():按指定步长增加
Decr()和DecrBy()方法是对数字进行减的操作，和Incr正好相反

Append():追加
Append()表示往字符串后面追加元素，返回值是字符串的总长度

StrLen():获取长度
StrLen()方法可以获取字符串的长度

列表(list)类型
LPush():将元素压入链表
可以使用LPush()方法将数据从左侧压入链表

集合(set)类型
SAdd():添加元素

HSet():设置
HSet()方法支持如下格式
*/
func HashSet[T any](rdb *redis.Client, hashName, key string, value T) int64 {
	ctx := context.Background()
	marshal, err := constant.Json.Marshal(&value)
	if err != nil {
		panic(err)
	}
	result, err := rdb.HSet(ctx, hashName, key, marshal).Result()
	if err != nil {
		panic(err)
	}
	return result
}

func HashSetM[M ~map[K]V, K comparable, V any](rdb *redis.Client, key string, mapValue M) int64 {
	var results = int64(0)
	ctx := context.Background()
	for k, v := range mapValue {
		bt, err := constant.Json.Marshal(&v)
		if err != nil {
			panic(err)
		}
		result, err := rdb.HSet(ctx, key, k, bt).Result()
		if err != nil {
			panic(err)
			return result
		}
		results += result
	}

	return results
}
func HashExists(ctx context.Context, rdb *redis.Client, key, field string) bool {
	res, err := rdb.HExists(ctx, key, field).Result()
	if err != nil {
		panic(err)
	}
	return res
}
func HashGet[T any](rdb *redis.Client, key, field string) (*T, bool) {
	ctx := context.Background()
	var err error
	if res := HashExists(ctx, rdb, key, field); !res {
		return nil, false
	}
	address, err := rdb.HGet(ctx, key, field).Result()
	if err != nil {
		panic(err)
		return nil, false
	}
	var result T
	err = constant.Json.Unmarshal([]byte(address), &result)
	if err != nil {
		panic(err)
		return nil, false
	}
	return &result, true

}
func HashDel(rdb *redis.Client, hashName string, key ...string) {
	ctx := context.Background()
	_, err := rdb.HDel(ctx, hashName, key...).Result()
	if err != nil {
		panic(err)
	}
}

func GetSetMembers(rdb *redis.Client, key string) {
	ctx := context.Background()

	res, err := rdb.SMembers(ctx, key).Result()
	if err != nil {
		panic(err)
	}

	fmt.Println(res)

}
func GetSetMapMembers(rdb *redis.Client, key string) {
	ctx := context.Background()
	res, err := rdb.SMembersMap(ctx, key).Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(res)

}
